package compilers.AST.basics;

import java.io.PrintStream;

import compilers.AST.Node;
import compilers.AST.expression.Term;
import compilers.AST.types.MyType;
import compilers.AST.types.TypeSpecifier;
import compilers.compiler.PGenerator;
import compilers.exceptions.SemanticalException;
import compilers.symbolTable.SymbolTableArrayElement;
import compilers.symbolTable.SymbolTableElement;
import compilers.symbolTable.SymbolTableScope;
import compilers.symbolTable.SymbolTableVariableElement;

public class Identifier extends Term {
	
	private String fValue;
	private TypeSpecifier fConvertTo = null;
	
	public Identifier(String value) {
		fValue = value;
	}
	
	public Identifier(String value, int lineNr) {
		super(lineNr);
		fValue = value;
	}
	
	public void print(int identLevel) {
		println(identLevel, "Identifier");
		println(identLevel+1, "Value = " + fValue);
	}
	
	public String getValue() {
		return fValue;
	}
	
	public void checkSemantics(SymbolTableScope currentScope) throws SemanticalException {
		if (currentScope.getElement(fValue, fNodeId) == null) {
			throw new SemanticalException("Identifier '" + fValue + "' is not declared.", fLineNr);
		}
	}
	
	public int getResultType(SymbolTableScope currentScope) {
		if(this.fConvertTo == null) {
			return currentScope.getElement(fValue, fNodeId).getType().getType(currentScope);
		} else {
			return this.fConvertTo.getType(currentScope);
		}
	}
	
	public int getOriginalType(SymbolTableScope currentScope) {
		return currentScope.getElement(fValue, fNodeId).getType().getType(currentScope);
	}

	public boolean isConstant(SymbolTableScope currentScope) {
		SymbolTableElement element = currentScope.getElement(fValue, fNodeId);
		if (element == null) {
			return false;
		}
		if (element instanceof SymbolTableVariableElement) {
			SymbolTableVariableElement vElement = (SymbolTableVariableElement) element;
			if ((vElement.isConstant()) && (vElement.getConstantValue() != null)) {
				return true;
			} else {
				return false;
			}
			
		} else {
			return false;
		}
		
	}

	public MyType calculateValue(SymbolTableScope currentScope) {
		SymbolTableVariableElement element = (SymbolTableVariableElement) currentScope.getElement(fValue, fNodeId);
		return element.getConstantValue();
	}
	
	public void generatePcode(PGenerator pStream, SymbolTableScope currentScope) {
		// Load value of identifier on top of the stack
		SymbolTableVariableElement element = (SymbolTableVariableElement) currentScope.getElement(fValue, fNodeId);
		pStream.lod(element.getType().getPType(currentScope), currentScope.getDepth(), element.getMemoryAddress());
		
		//Convert if necessary
		if(this.fConvertTo != null) {
			pStream.conv(TypeSpecifier.getPType(this.getOriginalType(currentScope)), this.fConvertTo.getPType(currentScope));
		}
	}
	
	public int getAddress(SymbolTableScope currentScope) {
		SymbolTableElement element = currentScope.getElement(fValue, fNodeId);
		if(element != null) {
			return element.getMemoryAddress();
		} else {
			return -1;
		}
	}
	
	public void setConvertTo(TypeSpecifier type) {
		this.fConvertTo = type;
	}
	
	public boolean isArray(SymbolTableScope currentScope) {
		SymbolTableElement element = currentScope.getElement(fValue, fNodeId);
		if (element instanceof SymbolTableArrayElement) {
			return true;
		}
		return false;
	}
	
	public String getArrayIdent(SymbolTableScope currentScope) {
		return fValue;
	}
}
